//头文件
#include <Arduino.h>
#include <U8g2lib.h>    //OLED屏幕驱动
#include <WiFi.h>       //WIFI相关函数
#include <RDA5807.h>    //FM收音
#include <SPI.h>        //SPI与OLED
#include <Wire.h>       //I2C与FM
#include <Ticker.h>

//wifi用户名与密码
char* ssid     = "iron2222";
char* password = "18436099009"; 

//网络时钟设置
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;

//OLED引脚设置
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 36, /* data=*/ 35, /* cs=*/ 37, /* dc=*/ 33, /* reset=*/ 34);

//I2C引脚设置
#define ESP32_I2C_SDA 5
#define ESP32_I2C_SCL 4
#define MAX_DELAY_RDS 40   // 40ms - polling method
long rds_elapsed = millis();
RDA5807 rx;

//网络收音
uint16_t num=0;
#define WEBSERVERIP "61.158.152.216"
#define WEBSERVERPORT 3999
uint8_t netbuf[3][1024];      //网络数据缓冲区
uint16_t writep = 0;          //写入数量
uint16_t readp = 0;           //读取数量
WiFiClient client;            //声明一个客户端对象，用于与服务器进行连接
bool connstat = false;        //连接状态
bool iswaitecho = false;      //是否等待服务器回应
Ticker flipper;               //时间中断
uint16_t m_offset = 0;

void onTimer(void) {
  if(readp<=writep)  dacWrite(17, netbuf[readp % 3][m_offset++]);      //播放一次声音
  if (m_offset >= 1024) {
    m_offset = 0;
    readp++;      //读取完成一个缓冲区
  }
}
bool connNetMusic() {
  uint8_t i = 0;
  while (i < 5) { //最多连接5次
    if (client.connect(WEBSERVERIP, WEBSERVERPORT)) {
      connstat = true;
      Serial.println("连接成功");
      return true;
    } else {
      Serial.println("访问失败");
      client.stop(); //关闭客户端
    }
    i++;
    delay(100);
  }
  return false;
}

void playMusic() {
        digitalWrite(41, HIGH);digitalWrite(42, HIGH);
        if (connstat == true){

            if (iswaitecho == false && (writep - readp) < 2) {
                client.write('n');          //申请一个缓冲片
                iswaitecho = true;
            }
            if (client.available()) { //如果有数据可读取
                num = client.read(netbuf[writep % 3], 1024);
                if (writep == 0 && readp == 0) {
                    flipper.attach(0, onTimer);     //开启定时中断 就是每秒中断20000次
                }
                writep++;
                iswaitecho = false;
            }
        }
}

//按键引脚配置
int button1Pin = 1;    
int button2Pin = 2;    
int button3Pin = 3;     
int button4Pin = 6;  

String ipaddress;

void setup() {
  Serial.begin(115200);                        //串口启动
  u8g2.begin();                                //OLED启动
  
  dispwelcome();                               //OLED显示开机界面

  
  //启动WiFi服务，这里是客户端
  WiFi.begin(ssid, password);                  
  Serial.print("Connecting to ");              // 串口监视器输出网络连接信息
  Serial.print(ssid); Serial.println(" ...");  // 告知用户正在尝试WiFi连接
  int i = 0;                                   // 这一段程序语句用于检查WiFi是否连接成功
  while (WiFi.status() != WL_CONNECTED) {      // WiFi.status()函数的返回值是由WiFi连接状态所决定的。 
    delay(1000);                               // 如果WiFi连接成功则返回值为WL_CONNECTED                       
    Serial.print(i++); Serial.print(' ');      // 此处通过While循环让NodeMCU每隔一秒钟检查一次WiFi.status()函数返回值
  }                                            // 同时NodeMCU将通过串口监视器输出连接时长读秒。
                                               // 这个读秒是通过变量i每隔一秒自加1来实现的。                                           
  Serial.println("");                          // WiFi连接成功后
  Serial.println("Connection established!");   // 将通过串口监视器输出"连接成功"信息。
  Serial.print("IP address:    ");             // 同时还将输出IP地址。这一功能是通过调用
  Serial.println(WiFi.localIP());              // WiFi.localIP()函数来实现的。该函数的返回值即NodeMCU的IP地址。
  ipaddress = WiFi.localIP().toString();


  // 从网络时间服务器上获取并设置时间，获取成功后芯片会使用RTC时钟保持时间的更新
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();


  // I2C相关设置
  Wire.begin(ESP32_I2C_SDA, ESP32_I2C_SCL);
  rx.setup();
  rx.setVolume(0);//音量大小设置，可调
  delay(500);
  //FM相关设置
  rx.setFrequency(10650); //开启是频率，可调
  //使能SDR
  rx.setRDS(true);

  connNetMusic();

  //按键模式设置为输入，上拉
  pinMode(button1Pin, INPUT_PULLUP); 
  pinMode(button2Pin, INPUT_PULLUP);
  pinMode(button3Pin, INPUT_PULLUP);
  pinMode(button4Pin, INPUT_PULLUP);
  //通道模式选择
  pinMode(41, OUTPUT);
  pinMode(42, OUTPUT);


}

uint8_t modelsign = 1; //设置状态标志位，默认为无输出

void loop() {

  //标志位1，开机动画+时钟
  if (modelsign == 1)
  {
    timer_show();       //开机logo之后，直接显示时间与IP地址
    //printLocalTime();
  }

  //按下KEY1，标志位+1
  if (digitalRead(button1Pin) == LOW)
   {
    delay(200);
    if (digitalRead(button1Pin) == LOW){
       modelsign++;      
      }
   }
  
  //标志位2，FM模式：KEY2换台，KEY3音量+，KEY4音量-
  if (modelsign == 2)
   {
    selectDevice(1);
    FM_show();
    if (digitalRead(button2Pin) == LOW)
    {
      rx.setFrequencyUp();
    }
    if (digitalRead(button3Pin) == LOW)
    {
      rx.setVolumeUp();
    }
    if (digitalRead(button4Pin) == LOW)
    {
      rx.setVolumeDown();
    }      
   } 

   //标志位3，网络收音模式
  if (modelsign == 3)
   {
    selectDevice(2);
    WebRadio_show();
    playMusic();
    }
}


//模式切换，0：时间显示 1:FM收音机 2:网络电台
void selectDevice(uint8_t dev) {
  if(dev == 0) {
    digitalWrite(41, LOW);
    digitalWrite(42, LOW);
  }
  if(dev == 1) {
    digitalWrite(41, HIGH);
    digitalWrite(42, LOW);
  }
  if(dev == 2) {
    digitalWrite(41, HIGH);
    digitalWrite(42, HIGH);
  }
}


//开机logo显示函数
void dispwelcome() {
  u8g2.firstPage();
  u8g2.clearBuffer();         //清除内部缓冲区
  u8g2.setFontDirection(0);   //设置字体方向
  u8g2.setFont(u8g2_font_ncenB14_tr);     //设置字体
  u8g2.setCursor(0, 40);
  u8g2.print("Hello Jarvis!");
  u8g2.sendBuffer();
  delay(1000);
}

//网络时钟显示函数
void timer_show() {
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
    {
        u8g2.clearBuffer();
        u8g2.setFontDirection(0);
        u8g2.setFont(u8g2_font_ncenB08_tr);
        u8g2.setCursor(0, 40);
        u8g2.print("Failed to obtain time");
        u8g2.setCursor(2,55);
        u8g2.print(ipaddress);
        u8g2.sendBuffer();
        return;
    }
  u8g2.clearBuffer();
  u8g2.setFontDirection(0);
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(20, 15);
  u8g2.print(&timeinfo, "%H:%M:%S");
  u8g2.setCursor(15, 35);
  u8g2.print(&timeinfo, "%Y/%m/%d");
  u8g2.setCursor(2,55);
  u8g2.print(ipaddress);
  u8g2.sendBuffer();
}

//串口打印时间函数
void printLocalTime()
{
    struct tm timeinfo;
    if (!getLocalTime(&timeinfo))
    {
        Serial.println("Failed to obtain time");
        return;
    }
    Serial.println(&timeinfo, "%F %T %A"); // 格式化输出
}



//FM模式显示函数
void FM_show() {
  char str[64] = {0};
  u8g2.firstPage();
  u8g2.clearBuffer();         //清除内部缓冲区
  u8g2.setFontDirection(0);   //设置字体方向
  
  u8g2.setFont(u8g2_font_ncenB14_tr);     //设置字体
  u8g2.setCursor(15, 20);
  u8g2.print("FM Radio");

  u8g2.setFont(u8g2_font_ncenB10_tf);  
  u8g2.setCursor(15, 40);
  sprintf(str, "%u MHz",rx.getFrequency() );
  u8g2.print(str);
  u8g2.setCursor(15, 55);
  sprintf(str, "Vol: %2.2u",rx.getVolume());
  u8g2.print(str);
  
  u8g2.sendBuffer();
  delay(1000);
}

//网络收音模块显示函数
void WebRadio_show(){
  char str[64] = {0};
  if (connstat == true) {
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_ncenB14_tr);
    u8g2.setCursor(10,20);
    u8g2.print("Net Radio");
    u8g2.setFont(u8g2_font_ncenB08_tf);
    u8g2.setCursor(38, 46);
    sprintf(str, "%s:%d", WEBSERVERIP, WEBSERVERPORT);
    u8g2.print(str);        
  }else {
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_ncenB14_tr); 
    u8g2.setCursor(10, 20);
    u8g2.print("Net Radio");
    u8g2.setFont(u8g2_font_ncenB08_tf);
    u8g2.setCursor(20, 40);
    u8g2.print("Connection failed.");
        }
   u8g2.sendBuffer();
  }
